/*
 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2013, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */


#include "DwarfTypeFactory.h"

#include <algorithm>
#include <new>

#include <AutoLocker.h>
#include <Variant.h>

#include "ArrayIndexPath.h"
#include "Architecture.h"
#include "CompilationUnit.h"
#include "DebugInfoEntries.h"
#include "Dwarf.h"
#include "DwarfFile.h"
#include "DwarfTargetInterface.h"
#include "DwarfUtils.h"
#include "DwarfTypes.h"
#include "GlobalTypeLookup.h"
#include "Register.h"
#include "RegisterMap.h"
#include "SourceLanguageInfo.h"
#include "StringUtils.h"
#include "Tracing.h"
#include "TypeLookupConstraints.h"
#include "ValueLocation.h"


namespace {


// #pragma mark - HasTypePredicate


template<typename EntryType>
struct HasTypePredicate {
	inline bool operator()(EntryType* entry) const
	{
		return entry->GetType() != NULL;
	}
};


// #pragma mark - HasReturnTypePredicate


template<typename EntryType>
struct HasReturnTypePredicate {
	inline bool operator()(EntryType* entry) const
	{
		return entry->ReturnType() != NULL;
	}
};


// #pragma mark - HasEnumeratorsPredicate


struct HasEnumeratorsPredicate {
	inline bool operator()(DIEEnumerationType* entry) const
	{
		return !entry->Enumerators().IsEmpty();
	}
};


// #pragma mark - HasDimensionsPredicate


struct HasDimensionsPredicate {
	inline bool operator()(DIEArrayType* entry) const
	{
		return !entry->Dimensions().IsEmpty();
	}
};


// #pragma mark - HasMembersPredicate


struct HasMembersPredicate {
	inline bool operator()(DIECompoundType* entry) const
	{
		return !entry->DataMembers().IsEmpty();
	}
};


// #pragma mark - HasBaseTypesPredicate


struct HasBaseTypesPredicate {
	inline bool operator()(DIEClassBaseType* entry) const
	{
		return !entry->BaseTypes().IsEmpty();
	}
};


// #pragma mark - HasTemplateParametersPredicate


struct HasTemplateParametersPredicate {
	inline bool operator()(DIEClassBaseType* entry) const
	{
		return !entry->TemplateParameters().IsEmpty();
	}
};


// #pragma mark - HasParametersPredicate


template<typename EntryType>
struct HasParametersPredicate {
	inline bool operator()(EntryType* entry) const
	{
		return !entry->Parameters().IsEmpty();
	}
};


// #pragma mark - HasLowerBoundPredicate


struct HasLowerBoundPredicate {
	inline bool operator()(DIESubrangeType* entry) const
	{
		return entry->LowerBound()->IsValid();
	}
};


// #pragma mark - HasUpperBoundPredicate


struct HasUpperBoundPredicate {
	inline bool operator()(DIESubrangeType* entry) const
	{
		return entry->UpperBound()->IsValid();
	}
};


// #pragma mark - HasCountPredicate


struct HasCountPredicate {
	inline bool operator()(DIESubrangeType* entry) const
	{
		return entry->Count()->IsValid();
	}
};


// #pragma mark - HasContainingTypePredicate


struct HasContainingTypePredicate {
	inline bool operator()(DIEPointerToMemberType* entry) const
	{
		return entry->ContainingType() != NULL;
	}
};


}	// unnamed namespace


// #pragma mark - ArtificialIntegerType


class DwarfTypeFactory::ArtificialIntegerType : public PrimitiveType {
public:
	ArtificialIntegerType(const BString& id, const BString& name,
		target_size_t byteSize, uint32 typeConstant)
		:
		fID(id),
		fName(name),
		fByteSize(byteSize),
		fTypeConstant(typeConstant)
	{
	}

	static status_t Create(target_size_t byteSize, bool isSigned, Type*& _type)
	{
		// get the matching type constant
		uint32 typeConstant;
		switch (byteSize) {
			case 1:
				typeConstant = isSigned ? B_INT8_TYPE : B_UINT8_TYPE;
				break;
			case 2:
				typeConstant = isSigned ? B_INT16_TYPE : B_UINT16_TYPE;
				break;
			case 4:
				typeConstant = isSigned ? B_INT32_TYPE : B_UINT32_TYPE;
				break;
			case 8:
				typeConstant = isSigned ? B_INT64_TYPE : B_UINT64_TYPE;
				break;
			default:
				return B_BAD_VALUE;
		}

		// name and ID
		char buffer[16];
		snprintf(buffer, sizeof(buffer), isSigned ? "int%d" : "uint%d",
			(int)byteSize * 8);
		BString id(buffer);
		if (id.Length() == 0)
			return B_NO_MEMORY;

		// create the type
		ArtificialIntegerType* type = new(std::nothrow) ArtificialIntegerType(
			id, id, byteSize, typeConstant);
		if (type == NULL)
			return B_NO_MEMORY;

		_type = type;
		return B_OK;
	}

	virtual image_id ImageID() const
	{
		return -1;
	}

	virtual const BString& ID() const
	{
		return fID;
	}

	virtual const BString& Name() const
	{
		return fName;
	}

	virtual target_size_t ByteSize() const
	{
		return fByteSize;
	}

	virtual status_t ResolveObjectDataLocation(
		const ValueLocation& objectLocation, ValueLocation*& _location)
	{
		// TODO: Implement!
		return B_UNSUPPORTED;
	}

	virtual status_t ResolveObjectDataLocation(target_addr_t objectAddress,
		ValueLocation*& _location)
	{
		// TODO: Implement!
		return B_UNSUPPORTED;
	}

	virtual uint32 TypeConstant() const
	{
		return fTypeConstant;
	}

private:
	BString	fID;
	BString	fName;
	uint32	fByteSize;
	uint32	fTypeConstant;
};


// #pragma mark - DwarfTypeFactory


DwarfTypeFactory::DwarfTypeFactory(DwarfTypeContext* typeContext,
	GlobalTypeLookup* typeLookup, GlobalTypeCache* typeCache)
	:
	fTypeContext(typeContext),
	fTypeLookup(typeLookup),
	fTypeCache(typeCache)
{
	fTypeContext->AcquireReference();
	fTypeCache->AcquireReference();
}


DwarfTypeFactory::~DwarfTypeFactory()
{
	fTypeContext->ReleaseReference();
	fTypeCache->ReleaseReference();
}


status_t
DwarfTypeFactory::CreateType(DIEType* typeEntry, DwarfType*& _type)
{
	// try the type cache first
	BString name;
	DwarfUtils::GetFullyQualifiedDIEName(typeEntry, name);

	TypeLookupConstraints constraints(
		dwarf_tag_to_type_kind(typeEntry->Tag()));
	int32 subtypeKind = dwarf_tag_to_subtype_kind(typeEntry->Tag());
	if (subtypeKind >= 0)
		constraints.SetSubtypeKind(subtypeKind);

	AutoLocker<GlobalTypeCache> cacheLocker(fTypeCache);
	Type* globalType = name.Length() > 0
		? fTypeCache->GetType(name, constraints) : NULL;
	if (globalType == NULL) {
		// lookup by name failed -- try lookup by ID
		BString id;
		if (DwarfType::GetTypeID(typeEntry, id))
			globalType = fTypeCache->GetTypeByID(id);
	}

	if (globalType != NULL) {
		DwarfType* globalDwarfType = dynamic_cast<DwarfType*>(globalType);
		if (globalDwarfType != NULL) {
			globalDwarfType->AcquireReference();
			_type = globalDwarfType;
			return B_OK;
		}
	}

	cacheLocker.Unlock();

	// If the type entry indicates a declaration only, we try to look the
	// type up globally first.
	if (typeEntry->IsDeclaration() && name.Length() > 0
		&& fTypeLookup->GetType(fTypeCache, name,
			constraints, globalType)
			== B_OK) {
		DwarfType* globalDwarfType
			= dynamic_cast<DwarfType*>(globalType);
		if (globalDwarfType != NULL) {
			_type = globalDwarfType;
			return B_OK;
		}

		globalType->ReleaseReference();
	}

	// No luck yet -- create the type.
	DwarfType* type;
	status_t error = _CreateTypeInternal(name, typeEntry, type);
	if (error != B_OK)
		return error;
	BReference<DwarfType> typeReference(type, true);

	// Insert the type into the cache. Re-check, as the type may already
	// have been inserted (e.g. in the compound type case).
	cacheLocker.Lock();
	if (name.Length() > 0
			? fTypeCache->GetType(name, constraints) == NULL
			: fTypeCache->GetTypeByID(type->ID()) == NULL) {
		error = fTypeCache->AddType(type);
		if (error != B_OK)
			return error;
	}
	cacheLocker.Unlock();

	// try to get the type's size
	uint64 size;
	if (_ResolveTypeByteSize(typeEntry, size) == B_OK)
		type->SetByteSize(size);

	_type = typeReference.Detach();
	return B_OK;
}


status_t
DwarfTypeFactory::_CreateTypeInternal(const BString& name,
	DIEType* typeEntry, DwarfType*& _type)
{
	switch (typeEntry->Tag()) {
		case DW_TAG_class_type:
		case DW_TAG_structure_type:
		case DW_TAG_union_type:
		case DW_TAG_interface_type:
			return _CreateCompoundType(name,
				dynamic_cast<DIECompoundType*>(typeEntry),
				(compound_type_kind)dwarf_tag_to_subtype_kind(
					typeEntry->Tag()), _type);

		case DW_TAG_base_type:
			return _CreatePrimitiveType(name,
				dynamic_cast<DIEBaseType*>(typeEntry), _type);

		case DW_TAG_pointer_type:
			return _CreateAddressType(name,
				dynamic_cast<DIEAddressingType*>(typeEntry),
				DERIVED_TYPE_POINTER, _type);
		case DW_TAG_reference_type:
			return _CreateAddressType(name,
				dynamic_cast<DIEAddressingType*>(typeEntry),
				DERIVED_TYPE_REFERENCE, _type);

		case DW_TAG_const_type:
			return _CreateModifiedType(name,
				dynamic_cast<DIEModifiedType*>(typeEntry),
				TYPE_MODIFIER_CONST, _type);
		case DW_TAG_packed_type:
			return _CreateModifiedType(name,
				dynamic_cast<DIEModifiedType*>(typeEntry),
				TYPE_MODIFIER_PACKED, _type);
		case DW_TAG_volatile_type:
			return _CreateModifiedType(name,
				dynamic_cast<DIEModifiedType*>(typeEntry),
				TYPE_MODIFIER_VOLATILE, _type);
		case DW_TAG_restrict_type:
			return _CreateModifiedType(name,
				dynamic_cast<DIEModifiedType*>(typeEntry),
				TYPE_MODIFIER_RESTRICT, _type);
		case DW_TAG_shared_type:
			return _CreateModifiedType(name,
				dynamic_cast<DIEModifiedType*>(typeEntry),
				TYPE_MODIFIER_SHARED, _type);

		case DW_TAG_typedef:
			return _CreateTypedefType(name,
				dynamic_cast<DIETypedef*>(typeEntry), _type);

		case DW_TAG_array_type:
			return _CreateArrayType(name,
				dynamic_cast<DIEArrayType*>(typeEntry), _type);

		case DW_TAG_enumeration_type:
			return _CreateEnumerationType(name,
				dynamic_cast<DIEEnumerationType*>(typeEntry), _type);

		case DW_TAG_subrange_type:
			return _CreateSubrangeType(name,
				dynamic_cast<DIESubrangeType*>(typeEntry), _type);

		case DW_TAG_unspecified_type:
			return _CreateUnspecifiedType(name,
				dynamic_cast<DIEUnspecifiedType*>(typeEntry), _type);

		case DW_TAG_subroutine_type:
			return _CreateFunctionType(name,
				dynamic_cast<DIESubroutineType*>(typeEntry), _type);

		case DW_TAG_ptr_to_member_type:
			return _CreatePointerToMemberType(name,
				dynamic_cast<DIEPointerToMemberType*>(typeEntry), _type);

		case DW_TAG_string_type:
		case DW_TAG_file_type:
		case DW_TAG_set_type:
			// TODO: Implement (not relevant for C++)!
			return B_UNSUPPORTED;
	}

	return B_UNSUPPORTED;
}


status_t
DwarfTypeFactory::_CreateCompoundType(const BString& name,
	DIECompoundType* typeEntry, compound_type_kind compoundKind, DwarfType*& _type)
{
	TRACE_LOCALS("DwarfTypeFactory::_CreateCompoundType(\"%s\", %p, %d)\n",
		name.String(), typeEntry, compoundKind);

	// create the type
	DwarfCompoundType* type = new(std::nothrow) DwarfCompoundType(fTypeContext,
		name, typeEntry, compoundKind);
	if (type == NULL)
		return B_NO_MEMORY;
	BReference<DwarfCompoundType> typeReference(type, true);

	// Already add the type at this pointer to the cache, since otherwise
	// we could run into an infinite recursion when trying to create the types
	// for the data members.
// TODO: Since access to the type lookup context is multi-threaded, the
// incomplete type could become visible to other threads. Hence we keep the
// context locked, but that essentially kills multi-threading for this context.
	AutoLocker<GlobalTypeCache> cacheLocker(fTypeCache);
	status_t error = fTypeCache->AddType(type);
	if (error != B_OK)
{
printf("  -> failed to add type to cache\n");
		return error;
}
//	cacheLocker.Unlock();

	// find the abstract origin or specification that defines the data members
	DIECompoundType* memberOwnerEntry = DwarfUtils::GetDIEByPredicate(typeEntry,
		HasMembersPredicate());

	// create the data member objects
	if (memberOwnerEntry != NULL) {
		for (DebugInfoEntryList::ConstIterator it
					= memberOwnerEntry->DataMembers().GetIterator();
				DebugInfoEntry* _memberEntry = it.Next();) {
			DIEMember* memberEntry = dynamic_cast<DIEMember*>(_memberEntry);

			TRACE_LOCALS("  member %p\n", memberEntry);

			// get the type
			DwarfType* memberType;
			if (CreateType(memberEntry->GetType(), memberType) != B_OK)
				continue;
			BReference<DwarfType> memberTypeReference(memberType, true);

			// get the name
			BString memberName;
			DwarfUtils::GetDIEName(memberEntry, memberName);

			// create and add the member object
			DwarfDataMember* member = new(std::nothrow) DwarfDataMember(
				memberEntry, memberName, memberType);
			BReference<DwarfDataMember> memberReference(member, true);
			if (member == NULL || !type->AddDataMember(member)) {
				cacheLocker.Lock();
				fTypeCache->RemoveType(type);
				return B_NO_MEMORY;
			}
		}
	}

	// If the type is a class/struct/interface type, we also need to add its
	// base types, and possibly template parameters.
	if (DIEClassBaseType* classTypeEntry
			= dynamic_cast<DIEClassBaseType*>(typeEntry)) {
		// find the abstract origin or specification that defines the base types
		classTypeEntry = DwarfUtils::GetDIEByPredicate(classTypeEntry,
			HasBaseTypesPredicate());

		// create the inheritance objects for the base types
		if (classTypeEntry != NULL) {
			for (DebugInfoEntryList::ConstIterator it
						= classTypeEntry->BaseTypes().GetIterator();
					DebugInfoEntry* _inheritanceEntry = it.Next();) {
				DIEInheritance* inheritanceEntry
					= dynamic_cast<DIEInheritance*>(_inheritanceEntry);

				// get the type
				DwarfType* baseType;
				if (CreateType(inheritanceEntry->GetType(), baseType) != B_OK)
					continue;
				BReference<DwarfType> baseTypeReference(baseType, true);

				// create and add the inheritance object
				DwarfInheritance* inheritance = new(std::nothrow)
					DwarfInheritance(inheritanceEntry, baseType);
				BReference<DwarfInheritance> inheritanceReference(inheritance,
					true);
				if (inheritance == NULL || !type->AddInheritance(inheritance)) {
					cacheLocker.Lock();
					fTypeCache->RemoveType(type);
					return B_NO_MEMORY;
				}
			}
		}

		// find the abstract origin or specification that defines the template
		// parameters
		classTypeEntry = DwarfUtils::GetDIEByPredicate(
			dynamic_cast<DIEClassBaseType*>(typeEntry),
			HasTemplateParametersPredicate());

		if (classTypeEntry != NULL) {
			for (DebugInfoEntryList::ConstIterator it
						= classTypeEntry->TemplateParameters()
							.GetIterator();
					DebugInfoEntry* _typeEntry = it.Next();) {
				DIETemplateTypeParameter* templateTypeEntry
					= dynamic_cast<DIETemplateTypeParameter*>(_typeEntry);
				DwarfType* templateType;
				if (templateTypeEntry != NULL) {
					if (templateTypeEntry->GetType() == NULL
						|| CreateType(templateTypeEntry->GetType(),
							templateType) != B_OK) {
						continue;
					}
				} else {
					DIETemplateValueParameter* templateValueEntry
						= dynamic_cast<DIETemplateValueParameter*>(_typeEntry);
					if (CreateType(templateValueEntry->GetType(), templateType)
						!= B_OK) {
						continue;
					}
				}
				BReference<DwarfType> templateTypeReference(templateType,
					true);
				DwarfTemplateParameter* parameter
					= new(std::nothrow) DwarfTemplateParameter(_typeEntry,
						templateType);
				if (parameter == NULL) {
					cacheLocker.Lock();
					fTypeCache->RemoveType(type);
					return B_NO_MEMORY;
				}

				if (!type->AddTemplateParameter(parameter)) {
					cacheLocker.Lock();
					fTypeCache->RemoveType(type);
					return B_NO_MEMORY;
				}
			}
		}
	}

	_type = typeReference.Detach();
	return B_OK;;
}


status_t
DwarfTypeFactory::_CreatePrimitiveType(const BString& name,
	DIEBaseType* typeEntry, DwarfType*& _type)
{
	const DynamicAttributeValue* byteSizeValue = typeEntry->ByteSize();
//	const DynamicAttributeValue* bitOffsetValue = typeEntry->BitOffset();
	const DynamicAttributeValue* bitSizeValue = typeEntry->BitSize();

	uint32 bitSize = 0;
	if (byteSizeValue->IsValid()) {
		BVariant value;
		status_t error = fTypeContext->File()->EvaluateDynamicValue(
			fTypeContext->GetCompilationUnit(),
			fTypeContext->AddressSize(), fTypeContext->IsBigEndian(),
			fTypeContext->SubprogramEntry(), byteSizeValue,
			fTypeContext->TargetInterface(),
			fTypeContext->InstructionPointer(), fTypeContext->FramePointer(),
			value);
		if (error == B_OK && value.IsInteger())
			bitSize = value.ToUInt32() * 8;
	} else if (bitSizeValue->IsValid()) {
		BVariant value;
		status_t error = fTypeContext->File()->EvaluateDynamicValue(
			fTypeContext->GetCompilationUnit(),
			fTypeContext->AddressSize(), fTypeContext->IsBigEndian(),
			fTypeContext->SubprogramEntry(), bitSizeValue,
			fTypeContext->TargetInterface(),
			fTypeContext->InstructionPointer(), fTypeContext->FramePointer(),
			value);
		if (error == B_OK && value.IsInteger())
			bitSize = value.ToUInt32();
	}

	// determine type constant
	uint32 typeConstant = 0;
	switch (typeEntry->Encoding()) {
		case DW_ATE_boolean:
			typeConstant = B_BOOL_TYPE;
			break;

		case DW_ATE_float:
			switch (bitSize) {
				case 32:
					typeConstant = B_FLOAT_TYPE;
					break;
				case 64:
					typeConstant = B_DOUBLE_TYPE;
					break;
			}
			break;

		case DW_ATE_signed:
		case DW_ATE_signed_char:
			switch (bitSize) {
				case 8:
					typeConstant = B_INT8_TYPE;
					break;
				case 16:
					typeConstant = B_INT16_TYPE;
					break;
				case 32:
					typeConstant = B_INT32_TYPE;
					break;
				case 64:
					typeConstant = B_INT64_TYPE;
					break;
			}
			break;

		case DW_ATE_address:
		case DW_ATE_unsigned:
		case DW_ATE_unsigned_char:
			switch (bitSize) {
				case 8:
					typeConstant = B_UINT8_TYPE;
					break;
				case 16:
					typeConstant = B_UINT16_TYPE;
					break;
				case 32:
					typeConstant = B_UINT32_TYPE;
					break;
				case 64:
					typeConstant = B_UINT64_TYPE;
					break;
			}
			break;

		case DW_ATE_complex_float:
		case DW_ATE_imaginary_float:
		case DW_ATE_packed_decimal:
		case DW_ATE_numeric_string:
		case DW_ATE_edited:
		case DW_ATE_signed_fixed:
		case DW_ATE_unsigned_fixed:
		case DW_ATE_decimal_float:
		default:
			break;
	}

	// create the type
	DwarfPrimitiveType* type = new(std::nothrow) DwarfPrimitiveType(
		fTypeContext, name, typeEntry, typeConstant);
	if (type == NULL)
		return B_NO_MEMORY;

	_type = type;
	return B_OK;
}


status_t
DwarfTypeFactory::_CreateAddressType(const BString& name,
	DIEAddressingType* typeEntry, address_type_kind addressKind,
	DwarfType*& _type)
{
	// get the base type entry
	DIEAddressingType* baseTypeOwnerEntry = DwarfUtils::GetDIEByPredicate(
		typeEntry, HasTypePredicate<DIEAddressingType>());

	// create the base type
	DwarfType* baseType;
	if (baseTypeOwnerEntry != NULL) {
		status_t error = CreateType(baseTypeOwnerEntry->GetType(), baseType);
		if (error != B_OK)
			return error;
	} else {
		// According to the DWARF 3 specs a modified type *has* a base type.
		// GCC 4 doesn't (always?) bother to add one for "void".
		// TODO: We should probably search for a respective type by name. ATM
		// we just create a DwarfUnspecifiedType without DIE.
		TRACE_LOCALS("no base type for address type entry -- creating "
			"unspecified type\n");
		baseType = new(std::nothrow) DwarfUnspecifiedType(fTypeContext, "void",
			NULL);
		if (baseType == NULL)
			return B_NO_MEMORY;
	}
	BReference<Type> baseTypeReference(baseType, true);

	DwarfAddressType* type = new(std::nothrow) DwarfAddressType(fTypeContext,
		name, typeEntry, addressKind, baseType);
	if (type == NULL)
		return B_NO_MEMORY;

	_type = type;
	return B_OK;
}


status_t
DwarfTypeFactory::_CreateModifiedType(const BString& name,
	DIEModifiedType* typeEntry, uint32 modifiers, DwarfType*& _type)
{
	// Get the base type entry. If it is a modified type too or a typedef,
	// collect all modifiers and iterate until hitting an actual base type.
	DIEType* baseTypeEntry = NULL;
	DwarfType* baseType = NULL;
	while (true) {
		DIEModifiedType* baseTypeOwnerEntry = DwarfUtils::GetDIEByPredicate(
			typeEntry, HasTypePredicate<DIEModifiedType>());
		if (baseTypeOwnerEntry == NULL) {
			if (typeEntry->GetType() == NULL) {
				// in the case of a modified type that points to an
				// unspecified type (i.e. const void* in C/C++),
				// gcc appears to omit the base type attribute entirely.
				status_t result = _CreateUnspecifiedType(name,
					NULL, baseType);
				if (result != B_OK)
					return result;
				break;
			} else
				return B_BAD_VALUE;
		} else
			baseTypeEntry = baseTypeOwnerEntry->GetType();

		// resolve a typedef
		if (baseTypeEntry->Tag() == DW_TAG_typedef) {
			status_t error = _ResolveTypedef(
				dynamic_cast<DIETypedef*>(baseTypeEntry), baseTypeEntry);
			if (error != B_OK)
				return error;
		}

		if (baseTypeEntry == NULL)
			return B_BAD_VALUE;

		// If the base type is a modified type, too, resolve it.
		switch (baseTypeEntry->Tag()) {
			case DW_TAG_const_type:
				modifiers |= TYPE_MODIFIER_CONST;
				baseTypeOwnerEntry
					= dynamic_cast<DIEModifiedType*>(baseTypeEntry);
				continue;
			case DW_TAG_packed_type:
				modifiers |= TYPE_MODIFIER_PACKED;
				baseTypeOwnerEntry
					= dynamic_cast<DIEModifiedType*>(baseTypeEntry);
				continue;
			case DW_TAG_volatile_type:
				modifiers |= TYPE_MODIFIER_VOLATILE;
				baseTypeOwnerEntry
					= dynamic_cast<DIEModifiedType*>(baseTypeEntry);
				continue;
			case DW_TAG_restrict_type:
				modifiers |= TYPE_MODIFIER_RESTRICT;
				baseTypeOwnerEntry
					= dynamic_cast<DIEModifiedType*>(baseTypeEntry);
				continue;
			case DW_TAG_shared_type:
				modifiers |= TYPE_MODIFIER_SHARED;
				baseTypeOwnerEntry
					= dynamic_cast<DIEModifiedType*>(baseTypeEntry);
				continue;

			default:
				break;
		}

		// If we get here, we've found an actual base type.
		break;
	}

	if (baseType == NULL) {
		// create the base type
		status_t error = CreateType(baseTypeEntry, baseType);
		if (error != B_OK)
			return error;
	}

	BReference<Type> baseTypeReference(baseType, true);

	DwarfModifiedType* type = new(std::nothrow) DwarfModifiedType(fTypeContext,
		name, typeEntry, modifiers, baseType);
	if (type == NULL)
		return B_NO_MEMORY;

	_type = type;
	return B_OK;
}


status_t
DwarfTypeFactory::_CreateTypedefType(const BString& name,
	DIETypedef* typeEntry, DwarfType*& _type)
{
	// resolve the base type
	DIEType* baseTypeEntry;
	status_t error = _ResolveTypedef(typeEntry, baseTypeEntry);
	if (error != B_OK)
		return error;

	// create the base type
	DwarfType* baseType;
	error = CreateType(baseTypeEntry, baseType);
	if (error != B_OK)
		return error;
	BReference<Type> baseTypeReference(baseType, true);

	DwarfTypedefType* type = new(std::nothrow) DwarfTypedefType(fTypeContext,
		name, typeEntry, baseType);
	if (type == NULL)
		return B_NO_MEMORY;

	_type = type;
	return B_OK;
}


status_t
DwarfTypeFactory::_CreateArrayType(const BString& name,
	DIEArrayType* typeEntry, DwarfType*& _type)
{
	TRACE_LOCALS("DwarfTypeFactory::_CreateArrayType(\"%s\", %p)\n",
		name.String(), typeEntry);

	// create the base type
	DIEArrayType* baseTypeOwnerEntry = DwarfUtils::GetDIEByPredicate(
		typeEntry, HasTypePredicate<DIEArrayType>());
	if (baseTypeOwnerEntry == NULL) {
		WARNING("Failed to get base type for array type \"%s\"\n",
			name.String());
		return B_BAD_VALUE;
	}

	DwarfType* baseType = NULL;
	status_t error = CreateType(baseTypeOwnerEntry->GetType(), baseType);
	if (error != B_OK) {
		WARNING("Failed to create base type for array type \"%s\": %s\n",
			name.String(), strerror(error));
		return error;
	}
	BReference<Type> baseTypeReference(baseType, true);

	// create the array type
	DwarfArrayType* type = new(std::nothrow) DwarfArrayType(fTypeContext, name,
		typeEntry, baseType);
	if (type == NULL)
		return B_NO_MEMORY;
	BReference<DwarfType> typeReference(type, true);

	// add the array dimensions
	DIEArrayType* dimensionOwnerEntry = DwarfUtils::GetDIEByPredicate(
		typeEntry, HasDimensionsPredicate());

	if (dimensionOwnerEntry == NULL) {
		WARNING("Failed to get dimensions for array type \"%s\"\n",
			name.String());
		return B_BAD_VALUE;
	}

	for (DebugInfoEntryList::ConstIterator it
				= dimensionOwnerEntry->Dimensions().GetIterator();
			DebugInfoEntry* _dimensionEntry = it.Next();) {
		DIEType* dimensionEntry = dynamic_cast<DIEType*>(_dimensionEntry);

		// get/create the dimension type
		DwarfType* dimensionType = NULL;
		status_t error = CreateType(dimensionEntry, dimensionType);
		if (error != B_OK) {
			WARNING("Failed to create type for array dimension: %s\n",
				strerror(error));
			return error;
		}
		BReference<Type> dimensionTypeReference(dimensionType, true);

		// create and add the array dimension object
		DwarfArrayDimension* dimension
			= new(std::nothrow) DwarfArrayDimension(dimensionType);
		BReference<DwarfArrayDimension> dimensionReference(dimension, true);
		if (dimension == NULL || !type->AddDimension(dimension))
			return B_NO_MEMORY;
	}

	_type = typeReference.Detach();
	return B_OK;
}


status_t
DwarfTypeFactory::_CreateEnumerationType(const BString& name,
	DIEEnumerationType* typeEntry, DwarfType*& _type)
{
	// create the base type (it's optional)
	DIEEnumerationType* baseTypeOwnerEntry = DwarfUtils::GetDIEByPredicate(
		typeEntry, HasTypePredicate<DIEEnumerationType>());

	DwarfType* baseType = NULL;
	if (baseTypeOwnerEntry != NULL) {
		status_t error = CreateType(baseTypeOwnerEntry->GetType(), baseType);
		if (error != B_OK)
			return error;
	}
	BReference<Type> baseTypeReference(baseType, true);

	// create the enumeration type
	DwarfEnumerationType* type = new(std::nothrow) DwarfEnumerationType(
		fTypeContext, name, typeEntry, baseType);
	if (type == NULL)
		return B_NO_MEMORY;
	BReference<DwarfEnumerationType> typeReference(type, true);

	// get the enumeration values
	DIEEnumerationType* enumeratorOwnerEntry = DwarfUtils::GetDIEByPredicate(
		typeEntry, HasEnumeratorsPredicate());

	if (enumeratorOwnerEntry != NULL) {
		for (DebugInfoEntryList::ConstIterator it
					= enumeratorOwnerEntry->Enumerators().GetIterator();
				DebugInfoEntry* _enumeratorEntry = it.Next();) {
			DIEEnumerator* enumeratorEntry = dynamic_cast<DIEEnumerator*>(
				_enumeratorEntry);

			// evaluate the value
			BVariant value;
			status_t error = fTypeContext->File()->EvaluateConstantValue(
				fTypeContext->GetCompilationUnit(),
				fTypeContext->AddressSize(), fTypeContext->IsBigEndian(),
				fTypeContext->SubprogramEntry(), enumeratorEntry->ConstValue(),
				fTypeContext->TargetInterface(),
				fTypeContext->InstructionPointer(),
				fTypeContext->FramePointer(), value);
			if (error != B_OK) {
				// The value is probably not stored -- just ignore the
				// enumerator.
				TRACE_LOCALS("Failed to get value for enum type value %s::%s\n",
					name.String(), enumeratorEntry->Name());
				continue;
			}

			// create and add the enumeration value object
			DwarfEnumeratorValue* enumValue
				= new(std::nothrow) DwarfEnumeratorValue(enumeratorEntry,
					enumeratorEntry->Name(), value);
			BReference<DwarfEnumeratorValue> enumValueReference(enumValue,
				true);
			if (enumValue == NULL || !type->AddValue(enumValue))
				return B_NO_MEMORY;
		}
	}

	_type = typeReference.Detach();
	return B_OK;
}


status_t
DwarfTypeFactory::_CreateSubrangeType(const BString& name,
	DIESubrangeType* typeEntry, DwarfType*& _type)
{
	// get the base type
	DIESubrangeType* baseTypeOwnerEntry = DwarfUtils::GetDIEByPredicate(
		typeEntry, HasTypePredicate<DIESubrangeType>());
	DIEType* baseTypeEntry = baseTypeOwnerEntry != NULL
		? baseTypeOwnerEntry->GetType() : NULL;

	// get the lower bound
	BVariant lowerBound;
	DIESubrangeType* lowerBoundOwnerEntry = DwarfUtils::GetDIEByPredicate(
		typeEntry, HasLowerBoundPredicate());
	if (lowerBoundOwnerEntry != NULL) {
		// evaluate it
		DIEType* valueType;
		status_t error = fTypeContext->File()->EvaluateDynamicValue(
			fTypeContext->GetCompilationUnit(),
			fTypeContext->AddressSize(), fTypeContext->IsBigEndian(),
			fTypeContext->SubprogramEntry(),
			lowerBoundOwnerEntry->LowerBound(),
			fTypeContext->TargetInterface(),
			fTypeContext->InstructionPointer(),
			fTypeContext->FramePointer(), lowerBound, &valueType);
		if (error != B_OK) {
			WARNING("  failed to evaluate lower bound: %s\n", strerror(error));
			return error;
		}

		// If we don't have a base type yet, and the lower bound attribute
		// refers to an object, the type of that object is our base type.
		if (baseTypeEntry == NULL)
			baseTypeEntry = valueType;
	} else {
		// that's ok -- use the language default
		lowerBound.SetTo(fTypeContext->GetCompilationUnit()->SourceLanguage()
			->subrangeLowerBound);
	}

	// get the upper bound
	BVariant upperBound;
	DIESubrangeType* upperBoundOwnerEntry = DwarfUtils::GetDIEByPredicate(
		typeEntry, HasUpperBoundPredicate());
	if (upperBoundOwnerEntry != NULL) {
		// evaluate it
		DIEType* valueType;
		status_t error = fTypeContext->File()->EvaluateDynamicValue(
			fTypeContext->GetCompilationUnit(),
			fTypeContext->AddressSize(), fTypeContext->IsBigEndian(),
			fTypeContext->SubprogramEntry(),
			upperBoundOwnerEntry->UpperBound(),
			fTypeContext->TargetInterface(),
			fTypeContext->InstructionPointer(), fTypeContext->FramePointer(),
			upperBound, &valueType);
		if (error != B_OK) {
			WARNING("  failed to evaluate upper bound: %s\n", strerror(error));
			return error;
		}

		// If we don't have a base type yet, and the upper bound attribute
		// refers to an object, the type of that object is our base type.
		if (baseTypeEntry == NULL)
			baseTypeEntry = valueType;
	} else {
		// get the count instead
		DIESubrangeType* countOwnerEntry = DwarfUtils::GetDIEByPredicate(
			typeEntry, HasCountPredicate());
		if (countOwnerEntry != NULL) {
			// evaluate it
			BVariant count;
			DIEType* valueType;
			status_t error = fTypeContext->File()->EvaluateDynamicValue(
				fTypeContext->GetCompilationUnit(),
				fTypeContext->AddressSize(), fTypeContext->IsBigEndian(),
				fTypeContext->SubprogramEntry(),
				countOwnerEntry->Count(), fTypeContext->TargetInterface(),
				fTypeContext->InstructionPointer(),
				fTypeContext->FramePointer(), count, &valueType);
			if (error != B_OK) {
				WARNING("  failed to evaluate count: %s\n", strerror(error));
				return error;
			}

			// If we don't have a base type yet, and the count attribute refers
			// to an object, the type of that object is our base type.
			if (baseTypeEntry == NULL)
				baseTypeEntry = valueType;

			// we only support integers
			bool isSigned;
			if (!lowerBound.IsInteger(&isSigned) || !count.IsInteger()) {
				WARNING("  count given for subrange type, but lower bound or "
					"count is not integer\n");
				return B_BAD_VALUE;
			}

			if (isSigned)
				upperBound.SetTo(lowerBound.ToInt64() + count.ToInt64() - 1);
			else
				upperBound.SetTo(lowerBound.ToUInt64() + count.ToUInt64() - 1);
		}
	}

	// create the base type
	Type* baseType = NULL;
	status_t error;
	if (baseTypeEntry != NULL) {
		DwarfType* dwarfBaseType;
		error = CreateType(baseTypeEntry, dwarfBaseType);
		baseType = dwarfBaseType;
	} else {
		// We still don't have a base type yet. In this case the base type is
		// supposed to be a signed integer type with the same size as an address
		// for that compilation unit.
		error = ArtificialIntegerType::Create(
			fTypeContext->GetCompilationUnit()->AddressSize(), true, baseType);
	}
	if (error != B_OK)
		return error;
	BReference<Type> baseTypeReference(baseType, true);

	// TODO: Support the thread scaling attribute!

	// create the type
	DwarfSubrangeType* type = new(std::nothrow) DwarfSubrangeType(fTypeContext,
		name, typeEntry, baseType, lowerBound, upperBound);
	if (type == NULL)
		return B_NO_MEMORY;

	_type = type;
	return B_OK;
}


status_t
DwarfTypeFactory::_CreateUnspecifiedType(const BString& name,
	DIEUnspecifiedType* typeEntry, DwarfType*& _type)
{
	DwarfUnspecifiedType* type = new(std::nothrow) DwarfUnspecifiedType(
		fTypeContext, name, typeEntry);
	if (type == NULL)
		return B_NO_MEMORY;

	_type = type;
	return B_OK;
}

status_t
DwarfTypeFactory::_CreateFunctionType(const BString& name,
	DIESubroutineType* typeEntry, DwarfType*& _type)
{
	// get the return type
	DIESubroutineType* returnTypeOwnerEntry = DwarfUtils::GetDIEByPredicate(
		typeEntry, HasReturnTypePredicate<DIESubroutineType>());

	// create the base type
	DwarfType* returnType = NULL;
	if (returnTypeOwnerEntry != NULL) {
		status_t error = CreateType(returnTypeOwnerEntry->ReturnType(),
			returnType);
		if (error != B_OK)
			return error;
	}
	BReference<Type> returnTypeReference(returnType, true);

	DwarfFunctionType* type = new(std::nothrow) DwarfFunctionType(fTypeContext,
		name, typeEntry, returnType);
	if (type == NULL)
		return B_NO_MEMORY;
	BReference<DwarfType> typeReference(type, true);

	// get the parameters
	DIESubroutineType* parameterOwnerEntry = DwarfUtils::GetDIEByPredicate(
		typeEntry, HasParametersPredicate<DIESubroutineType>());

	if (parameterOwnerEntry != NULL) {
		for (DebugInfoEntryList::ConstIterator it
					= parameterOwnerEntry->Parameters().GetIterator();
				DebugInfoEntry* _parameterEntry = it.Next();) {
			if (_parameterEntry->Tag() == DW_TAG_unspecified_parameters) {
				type->SetHasVariableArguments(true);
				continue;
			}

			DIEFormalParameter* parameterEntry
				= dynamic_cast<DIEFormalParameter*>(_parameterEntry);

			// get the type
			DIEFormalParameter* typeOwnerEntry = DwarfUtils::GetDIEByPredicate(
				parameterEntry, HasTypePredicate<DIEFormalParameter>());
			if (typeOwnerEntry == NULL)
				return B_BAD_VALUE;

			DwarfType* parameterType;
			status_t error = CreateType(typeOwnerEntry->GetType(),
				parameterType);
			if (error != B_OK)
				return error;
			BReference<DwarfType> parameterTypeReference(parameterType, true);

			// get the name
			BString parameterName;
			DwarfUtils::GetDIEName(parameterEntry, parameterName);

			// create and add the parameter object
			DwarfFunctionParameter* parameter
				= new(std::nothrow) DwarfFunctionParameter(parameterEntry,
					parameterName, parameterType);
			BReference<DwarfFunctionParameter> parameterReference(parameter,
				true);
			if (parameter == NULL || !type->AddParameter(parameter))
				return B_NO_MEMORY;
		}
	}


	_type = typeReference.Detach();
	return B_OK;
}


status_t
DwarfTypeFactory::_CreatePointerToMemberType(const BString& name,
	DIEPointerToMemberType* typeEntry, DwarfType*& _type)
{
	// get the containing and base type entries
	DIEPointerToMemberType* containingTypeOwnerEntry
		= DwarfUtils::GetDIEByPredicate(typeEntry,
			HasContainingTypePredicate());
	DIEPointerToMemberType* baseTypeOwnerEntry = DwarfUtils::GetDIEByPredicate(
		typeEntry, HasTypePredicate<DIEPointerToMemberType>());

	if (containingTypeOwnerEntry == NULL || baseTypeOwnerEntry == NULL) {
		WARNING("Failed to get containing or base type for pointer to member "
			"type \"%s\"\n", name.String());
		return B_BAD_VALUE;
	}

	// create the containing type
	DwarfType* containingType;
	status_t error = CreateType(containingTypeOwnerEntry->ContainingType(),
		containingType);
	if (error != B_OK)
		return error;
	BReference<Type> containingTypeReference(containingType, true);

	DwarfCompoundType* compoundContainingType
		= dynamic_cast<DwarfCompoundType*>(containingType);
	if (compoundContainingType == NULL) {
		WARNING("Containing type for pointer to member type \"%s\" is not a "
			"compound type.\n", name.String());
		return B_BAD_VALUE;
	}

	// create the base type
	DwarfType* baseType;
	error = CreateType(baseTypeOwnerEntry->GetType(), baseType);
	if (error != B_OK)
		return error;
	BReference<Type> baseTypeReference(baseType, true);

	// create the type object
	DwarfPointerToMemberType* type = new(std::nothrow) DwarfPointerToMemberType(
		fTypeContext, name, typeEntry, compoundContainingType, baseType);
	if (type == NULL)
		return B_NO_MEMORY;

	_type = type;
	return B_OK;
}


status_t
DwarfTypeFactory::_ResolveTypedef(DIETypedef* entry,
	DIEType*& _baseTypeEntry)
{
	while (true) {
		// resolve the base type, possibly following abstract origin or
		// specification
		DIETypedef* baseTypeOwnerEntry = DwarfUtils::GetDIEByPredicate(
			entry, HasTypePredicate<DIETypedef>());
		if (baseTypeOwnerEntry == NULL)
			return B_BAD_VALUE;

		DIEType* baseTypeEntry = baseTypeOwnerEntry->GetType();
		if (baseTypeEntry->Tag() != DW_TAG_typedef) {
			_baseTypeEntry = baseTypeEntry;
			return B_OK;
		}

		entry = dynamic_cast<DIETypedef*>(baseTypeEntry);
	}
}


status_t
DwarfTypeFactory::_ResolveTypeByteSize(DIEType* typeEntry,
	uint64& _size)
{
	TRACE_LOCALS("DwarfTypeFactory::_ResolveTypeByteSize(%p)\n",
		typeEntry);

	// get the size attribute
	const DynamicAttributeValue* sizeValue;

	while (true) {
		// resolve a typedef
		if (typeEntry->Tag() == DW_TAG_typedef) {
			TRACE_LOCALS("  resolving typedef...\n");

			status_t error = _ResolveTypedef(
				dynamic_cast<DIETypedef*>(typeEntry), typeEntry);
			if (error != B_OK)
				return error;
		}

		sizeValue = typeEntry->ByteSize();
		if (sizeValue != NULL && sizeValue->IsValid())
			break;

		// resolve abstract origin
		if (DIEType* abstractOrigin = dynamic_cast<DIEType*>(
				typeEntry->AbstractOrigin())) {
			TRACE_LOCALS("  resolving abstract origin (%p)...\n",
				abstractOrigin);

			typeEntry = abstractOrigin;
			sizeValue = typeEntry->ByteSize();
			if (sizeValue != NULL && sizeValue->IsValid())
				break;
		}

		// resolve specification
		if (DIEType* specification = dynamic_cast<DIEType*>(
				typeEntry->Specification())) {
			TRACE_LOCALS("  resolving specification (%p)...\n", specification);

			typeEntry = specification;
			sizeValue = typeEntry->ByteSize();
			if (sizeValue != NULL && sizeValue->IsValid())
				break;
		}

		// For some types we have a special handling. For modified types we
		// follow the base type, for address types we know the size anyway.
		TRACE_LOCALS("  nothing yet, special type handling\n");

		switch (typeEntry->Tag()) {
			case DW_TAG_const_type:
			case DW_TAG_packed_type:
			case DW_TAG_volatile_type:
			case DW_TAG_restrict_type:
			case DW_TAG_shared_type:
				typeEntry = dynamic_cast<DIEModifiedType*>(typeEntry)
					->GetType();

				TRACE_LOCALS("  following modified type -> %p\n", typeEntry);

				if (typeEntry == NULL)
					return B_ENTRY_NOT_FOUND;
				break;
			case DW_TAG_pointer_type:
			case DW_TAG_reference_type:
			case DW_TAG_ptr_to_member_type:
				_size = fTypeContext->GetCompilationUnit()->AddressSize();

				TRACE_LOCALS("  pointer/reference type: size: %" B_PRIu64 "\n",
					_size);

				return B_OK;
			default:
				return B_ENTRY_NOT_FOUND;
		}
	}

	TRACE_LOCALS("  found attribute\n");

	// get the actual value
	BVariant size;
	status_t error = fTypeContext->File()->EvaluateDynamicValue(
		fTypeContext->GetCompilationUnit(),
		fTypeContext->AddressSize(), fTypeContext->IsBigEndian(),
		fTypeContext->SubprogramEntry(), sizeValue,
		fTypeContext->TargetInterface(), fTypeContext->InstructionPointer(),
		fTypeContext->FramePointer(), size);
	if (error != B_OK) {
		TRACE_LOCALS("  failed to resolve attribute: %s\n", strerror(error));
		return error;
	}

	_size = size.ToUInt64();

	TRACE_LOCALS("  -> size: %" B_PRIu64 "\n", _size);

	return B_OK;
}
